package org.bud.excel.expot.reply;

import org.apache.poi.ss.usermodel.*;
import org.bud.data.bit.ValueInfo;
import org.bud.excel.expot.ask.ExportIterable;
import org.bud.excel.expot.bit.ExportAskBody;
import org.bud.excel.expot.bit.ExcelValueInfo;
import org.bud.excel.expot.constant.Constant;
import org.bud.excel.expot.exception.ExcelException;
import org.bud.data.bit.fmt.FmtType;
import org.bud.excel.expot.support.QueryDataExcelHandler;
import org.bud.util.bit.Maps;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author shaun
 * @date 2024/7/18
 * @instruction
 */
public class WorkbookWriter {
    private Workbook workbook;
    private List<ExportAskBody<?,?>> body;
    private Sheet sheet;
    private AtomicInteger rowsAtom;
    private final Map<String,CellStyle> cache = new ConcurrentHashMap<>();
    public WorkbookWriter(Workbook workbook, List<ExportAskBody<?,?>> body){
        this.workbook = workbook;
        this.body = body;
    }
    public void workSheet(ExportAskBody<?,?> body){
        this.sheet = workbook.createSheet(body.getSheet().getSheetName());
        this.rowsAtom = new AtomicInteger(0);
    }
    public void write(){
        for(ExportAskBody<?,?> o : body){
            QueryDataExcelHandler handler = new QueryDataExcelHandler();
            handler.doSupport(o.getSupport());
            workSheet(o);
            for (ExportIterable iter
                 = new ExportIterable().query(o.getQuery());
                 iter.hasNext();){
                int length = o.getSheet().getHeaderKeys().length;
                handler.handle(
                        o.getSheet().getHeaderKeys(),
                        Maps.newHashMap(o.getSheet().getHeaderKeys(),o.getSheet().getHeaderNames()),
                        consumer(this.sheet,this.rowsAtom,length - 1,true),false);
                handler.handle(
                        o.getSheet().getHeaderKeys(),
                        iter.next(),
                        consumer(this.sheet,length - 1,this.rowsAtom),true);
            }
        }
    }

    private Consumer<ValueInfo> consumer(Sheet sheet, int length, AtomicInteger ato){
        return consumer(sheet,ato,length,false);
    }

    private Consumer<ValueInfo> consumer(Sheet sheet, AtomicInteger ato, int length, boolean headers){
        return vo-> {
            ExcelValueInfo v = (ExcelValueInfo)vo;
            Row row = Optional.ofNullable(sheet.getRow(ato.get()))
                    .orElseGet(() -> sheet.createRow(ato.get()));
            if(v.getIndex() == length){
                ato.incrementAndGet();
            }
            Cell cell = row.createCell(v.getIndex());
            cell.setCellStyle(cellStyle(headers).apply(v));
            setCellValue(cell).accept(Optional.ofNullable(v.getValue())
                    .orElseGet(
                            Optional.ofNullable(v.getDefaultValueSupplier())
                                    .orElse(() -> null)
                    ));
        };
    }

    private Consumer<Object> setCellValue(Cell cell){
        return o -> {
            if(o == null)
                return;
            if(o instanceof String){
                cell.setCellValue((String)o);
                return;
            }
            if(o instanceof Double){
                cell.setCellValue((Double) o);
                return;
            }
            if(o instanceof Integer){
                cell.setCellValue((Integer) o);
                return;
            }
            throw new ExcelException("cell value need [string,double,int]");
        };
    }

    private Function<ValueInfo, CellStyle> cellStyle(boolean headers){
        return vo -> {
            ExcelValueInfo v = (ExcelValueInfo)vo;
            String key = Optional.ofNullable(Constant.HEADER_FMT_STRING)
                    .filter(o -> headers).orElse(v.getFormat());
            if(cache.get(key) == null){
                CellStyle cellStyle = workbook.createCellStyle();
                cellStyle.setDataFormat(workbook.createDataFormat().getFormat(
                        Optional.ofNullable(
                                Constant.FMT_STRING)
                                .filter(o -> headers).orElse(v.getFormat()))
                );
                if(headers){
                    cellStyle.setAlignment(HorizontalAlignment.CENTER);
                } else if(v.getFmtType() == FmtType.STRING){
                    cellStyle.setAlignment(HorizontalAlignment.LEFT);
                } else if(v.getFmtType() == FmtType.DOUBLE || v.getFmtType() == FmtType.INT){
                    cellStyle.setAlignment(HorizontalAlignment.RIGHT);
                }
                cache.put(key,cellStyle);
            }
            return cache.get(key);
        };
    }

}
